// 基本头文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

// 字符设备相关
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>

// I2C总线
#include <linux/i2c.h>

static struct i2c_client *i2c_client_dev;

/*
 * dev：i2c客户端传入匹配的dev即可
 * reg：要写入的寄存器地址
 * val：要写入的值
 * return 0表示成功其他表示失败
*/
static int i2c_demo_write_reg(struct i2c_client *dev, uint8_t reg, uint8_t val)
{
	uint8_t send_buf[2];
    struct i2c_msg msg;
 
    send_buf[0] = reg;                 	/* 寄存器首地址 */
    memcpy(&send_buf[1], &val, 1);      /* 将要写入的数据拷贝到发送缓冲数组里面，如果有多位可以改变函数添加len参数 */
 
    msg.addr = dev->addr;    			/* 设备地址 */
    msg.flags = 0;              		/* 标记为写数据 */
    msg.buf = send_buf;                	/* 要写入的数据缓冲区 */
    msg.len = 2;            			/* 要写入的数据长度：地址位+数据位 */
 
    return i2c_transfer(dev->adapter, &msg, 1);
}

/*
 * dev：i2c客户端传入匹配的dev即可
 * reg：要读取的寄存器地址
 * val：读取到的值
 * return 0表示成功其他表示失败
*/
static int i2c_demo_read_reg(struct i2c_client *dev, uint8_t reg, uint8_t *val)
{
	int ret;
    struct i2c_msg msg[2];
 
    /* msg[0]为发送要读取的首地址 */
    msg[0].addr = dev->addr;         	/* 设备地址 */
    msg[0].flags = 0;                   /* 标记为发送数据 */
    msg[0].buf = &reg;                  /* 读取的首地址 */
    msg[0].len = 1;                     /* reg长度*/
 
    /* msg[1]读取数据 */
    msg[1].addr = dev->addr;         	/* 设备地址 */
    msg[1].flags = I2C_M_RD;            /* 标记为读取数据*/
    msg[1].buf = val;                   /* 读取数据缓冲区 */
    msg[1].len = 1;                    	/* 要读取的数据长度*/
 
    ret = i2c_transfer(dev->adapter, msg, 2);
    if(ret == 2) {
        ret = 0;
    }else {
        printk("i2c_cline: 0x%02X read failed=%d reg=%06x\n",dev->addr, ret, reg);
        ret = -EREMOTEIO;
    }
    return ret;
}


static int i2c_demo_probe(struct i2c_client *pdev, const struct i2c_device_id *id)
{
	uint8_t ret; 
	i2c_client_dev = pdev;
	
	#define MPU6050_WHO_AM_I 0x75
	i2c_demo_read_reg(i2c_client_dev, MPU6050_WHO_AM_I, &ret);

	printk("%s %d  i2c_id = 0x%02X read_value:0x%02X \n", __FUNCTION__, __LINE__, pdev->addr, ret);

	return 0;
}

static int i2c_demo_remove(struct i2c_client *pdev)
{
	printk("%s %d\n", __FUNCTION__, __LINE__);
	return 0;
}

/* 传统匹配方式ID列表 */
static const struct i2c_device_id i2c_demo_id_table[] = {
    {"mpu6050", 0},  
};
 
/* 设备树匹配列表 */
static const struct of_device_id i2c_demo_of_match[] = {
    { .compatible = "device_tree, mpu6050" }, // 匹配设备树
};
 
/* i2c驱动结构体 */    
static struct i2c_driver i2c_demo_driver = {
    .probe = i2c_demo_probe,
    .remove = i2c_demo_remove,
    .driver = {
            .owner = THIS_MODULE,
               .name = "mpu6050",
               .of_match_table = i2c_demo_of_match, 
           },
    .id_table = i2c_demo_id_table,
};

/* 1. 入口函数 */
static int __init i2c_demo_init(void)
{	
	int ret = 0;
    ret = i2c_add_driver(&i2c_demo_driver);
    return ret;
}


/* 2. 出口函数 */
static void __exit i2c_demo_exit(void)
{
	i2c_del_driver(&i2c_demo_driver);
}

module_init(i2c_demo_init);
module_exit(i2c_demo_exit);


MODULE_AUTHOR("Kongjun"); // 作者
MODULE_DESCRIPTION("i2c driver"); // 驱动介绍可以不写
MODULE_LICENSE("GPL");// 开源协议